home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / QF2.ZIP / QF.C < prev    next >
C/C++ Source or Header  |  1995-04-10  |  70KB  |  2,057 lines

  1. /**********************************************************************\
  2. *                                                                      *
  3. *  QF.C -- Quickfire!                                                  *
  4. *                                                                      *
  5. *  Source code by Diana Gruber                                         *
  6. *                                                                      *
  7. *  Copyright 1993, 1995 Diana Gruber. All Rights Reserved.             *
  8. *  Last modified: April 10, 1995                                       *
  9. *                                                                      *
  10. *  This source code is provided "as is" without any warranties, etc.   *
  11. *                                                                      *
  12. *  This particular incarnation of Quickfire was written two ways.      *
  13. *  The first way uses regular Mode X scrolling, as documented in       *
  14. *  the Action Arcade Adventure Set book. To activate this scrolling    *
  15. *  method, the term "ModeX" must be defined. The easiest way to do     *
  16. *  this is to uncomment this line in the file DEFS.H:                  *
  17. *                                                                      *
  18. *  //#define ModeX                                                     *
  19. *                                                                      *
  20. *  The Mode X method works in real and protected mode, and should      *
  21. *  work with Borland C++ or Microsoft C/C++ with Fastgraph/Light.      *
  22. *  The second method involves virtual buffers and Mode 13h (19         *
  23. *  decimal). In this method, all the backgrounds are built in RAM      *
  24. *  and blitted to the screen. To see this method, comment out the      *
  25. *  #define ModeX, as above.                                            *
  26. *                                                                      *
  27. *  The Mode 13h method requires protected mode. I used Watcom C/C++    *
  28. *  10.0 with the Rational Systems (Tenberry) DOS/4GW DOS extender.     *
  29. *  The DOS extender run-time module (DOS4GW.EXE) must be in the path   *
  30. *  for this version to run. Fastgraph is required, Fastgraph/Light     *
  31. *  won't work.                                                         *
  32. *                                                                      *
  33. *  Results:                                                            *
  34. *                                                                      *
  35. *  In general, the Mode 13h method is faster than the Mode X method    *
  36. *  because the Mode X method waits for the vertical retrace. To turn   *
  37. *  off the wait for the vertical retrace, uncomment this line:         *
  38. *                                                                      *
  39. *  //   fg_waitvr(0);                                                  *
  40. *                                                                      *
  41. *  in the init_graphics() function in the LOADGAME.C file. When you    *
  42. *  run in Mode X with the retrace check off, the game is about 5%      *
  43. *  faster than in Mode 13h, but it flickers like crazy.                *
  44. *                                                                      *
  45. *  Files required to recompile:                                        *
  46. *                                                                      *
  47. *  QF.C      -- animation functions and function main                  *
  48. *  LOADGAME.C-- initialization, termination, etc.                      *
  49. *  DEFS.H    -- global declarations and definitions                    *
  50. *                                                                      *
  51. *  Files required to run:                                              *
  52. *                                                                      *
  53. *  QFX.EXE   -- the program (mode X version)                           *
  54. *  QF13H.EXE -- the program (mode 13h version)                         *
  55. *  QF.PCX    -- background tiles                                       *
  56. *  QF1.PCX   -- title screen                                           *
  57. *  QF.BMP    -- sprite bitmaps                                         *
  58. *  QF.LEV    -- level map data                                         *
  59. *                                                                      *
  60. *  More information about constructing a scrolling game is in the      *
  61. *  book Action Arcade Adventure Set Coriolis Group, 1994) by Diana     *
  62. *  Gruber.                                                             *
  63. *                                                                      *
  64. \**********************************************************************/
  65.  
  66. #include "defs.h"
  67.  
  68. /* uncomment this line if you want to use the put_tile macro instead
  69.    of the put_tile function */
  70.    
  71. // #define put_tile(i,j) copytile((int)backtile[i+tile_orgx][j+tile_orgy],i,j,hidden);
  72.  
  73. /**********************************************************************\
  74. *                                                                      *
  75. *  main                                                                *
  76. *                                                                      *
  77. \**********************************************************************/
  78.  
  79. void main()
  80. {
  81.    register short i,j;
  82.    unsigned long time1, time2;
  83.  
  84.    fg_initpm();
  85.  
  86.    /* Autodetect VGA.  If it isn't there, exit. */
  87.  
  88. #ifdef ModeX
  89.    if ((fg_testmode(20,4) == 0))
  90. #else
  91.    if ((fg_testmode(19,1) == 0))
  92. #endif
  93.    {
  94.       printf("\nvga required\n");
  95.       exit(0);
  96.    }
  97.  
  98.    /* initialize the graphics environment */
  99.    init_graphics();
  100.  
  101.    /* load the keyboard handler */
  102.    fg_kbinit(1);
  103.  
  104.    /* load the level data */
  105.    load_level();
  106.  
  107.    /* load the sprite data */
  108.    load_sprite();
  109.  
  110.    /* initialize some global variables */
  111.    init_globals();
  112.  
  113.    /* start the intro sequence */
  114.    intro_screens();
  115.  
  116.    /* build the first screen of tiles on the hidden page */
  117.    tile_orgx = 0;
  118.    screen_orgx = 16;
  119.    tile_orgy = 6;
  120.    screen_orgy = 16;
  121.    for (i = 0; i < 22; i++)
  122.       for (j = 0; j < 15; j++)
  123.          put_tile(i,j);
  124.  
  125.    /* position the visual screen on the launch tube */
  126.    warp(0,96);
  127.  
  128.    /* start Quickfire hurtling down the launch tube */
  129.    launch_sequence();
  130.  
  131.    /* initialize the frame count here */
  132.    frames = 0;
  133.    fg_waitfor(1);
  134.    time1 = fg_getclock();
  135.  
  136.    /* start the action -- play the game */
  137.    activate_level();
  138.  
  139.    /* count the frames and exit now */
  140.    time2 = fg_getclock();
  141.    time1 = ((time2-time1) * 10) /182;
  142.    if (time1 > 0)
  143.       frames = frames/time1;
  144.    sprintf(abort_string,"%lu frames per second \n\n",frames);
  145.    terminate_game();
  146. }
  147.  
  148. /**********************************************************************\
  149. *                                                                      *
  150. *  activate_level                                                      *
  151. *  This is the main control function for Quickfire.  It controls       *
  152. *  each frame of animation, and in fact a frame is defined as one      *
  153. *  iteration of the main loop of this function.  The whole function    *
  154. *  is just a continuous loop, repeating the sequence of activating     *
  155. *  the sprites, redrawing the tiles, drawing the sprites, and          *
  156. *  flipping the pages.                                                 *
  157. *                                                                      *
  158. \**********************************************************************/
  159.  
  160. void near activate_level()
  161. {
  162.    OBJp node;
  163.    OBJp nextnode;
  164.  
  165.    for(;;)
  166.    {
  167.       /* increment the frame count for the benchmark test */
  168.  
  169.       frames++;
  170.  
  171.       /* Generate a random number for use later */
  172.       random_number = irandom(0,1000);
  173.  
  174.       /* spawn a new enemy intermittently based on the hit value */
  175.       if (random_number > hit_value)
  176.          start_enemy();
  177.  
  178.       /* traverse the linked list the first time, doing all the
  179.          action functions which update values, but don't draw anything */
  180.       for (node=bottom_node; node!=(OBJp)NULL; node=nextnode)
  181.       {
  182.          nextnode = node->next;
  183.          node->action(node);
  184.       }
  185.       player_go();
  186.       score->action(score);
  187.  
  188.       /* draw the tiles that have changed on the hidden page */
  189.       rebuild_hidden();
  190.  
  191.       /* apply all the bitmaps on the hidden screen */
  192.       apply_sprite(player);
  193.       for (node=bottom_node; node!=(OBJp)NULL; node=node->next)
  194.          apply_sprite(node);
  195.       apply_sprite(score);
  196.  
  197.       /* do a page flip */
  198.       swap();
  199.       if (scrolled)
  200.       {
  201.          page_copy();
  202.          scrolled = FALSE;
  203.       }
  204.  
  205.       /* check the keyboard handler, exit if escape key pressed */
  206.       if (fg_kbtest(KB_ESC))
  207.          return;
  208.    }
  209. }
  210.  
  211. /**********************************************************************\
  212. *                                                                      *
  213. *  adjust_layout                                                       *
  214. *  When you scroll the background, you need to keep track of which     *
  215. *  tiles have changed.  This data is stored in the layout arrays.      *
  216. *  When the screen shifts the layout arrays must be shifted by an      *
  217. *  equal amount.                                                       *
  218. *                                                                      *
  219. \**********************************************************************/
  220.  
  221. void near adjust_layout_down()
  222. {
  223.    register short i;
  224.    for (i = 0; i < 22; i++)
  225.          memcpy(layout[hidden][i],&layout[visual][i][1],14);
  226. }
  227.  
  228. void near adjust_layout_right()
  229. {
  230.    register short i;
  231.    for (i = 0; i < 20; i++)
  232.       memcpy(layout[hidden][i],layout[visual][i+2],15);
  233. }
  234.  
  235. void near adjust_layout_up()
  236. {
  237.    register short i;
  238.    for (i = 0; i < 22; i++)
  239.       memcpy(&layout[hidden][i][1],layout[visual][i],14);
  240. }
  241.  
  242. /**********************************************************************\
  243. *                                                                      *
  244. *  apply_sprite                                                        *
  245. *  There are three steps to applying the sprite to the background.     *
  246. *  First you must calculate which tiles are overwritten. Then you      *
  247. *  need to update the layout array for those tiles. Finally you can    *
  248. *  display the bitmap.                                                 *
  249. *                                                                      *
  250. \**********************************************************************/
  251.  
  252. void near apply_sprite(OBJp objp)
  253. {
  254.    register short i,j;
  255.    short x,y;
  256.    short tile_x,tile_y;
  257.    short max_tilex, min_tiley;
  258.    char *p;
  259.  
  260.    /* find the current x and y position of the sprite */
  261.  
  262.    x = objp->x + objp->image->xoffset;
  263.    y = objp->y + objp->image->yoffset;
  264.  
  265.    /* this is the tile at the lower left corner of the sprite */
  266.  
  267.    tile_x = x/16 - tile_orgx;
  268.    tile_y = y/16 - tile_orgy;
  269.  
  270.    /* how many tiles total need to be replaced depends on the size 
  271.       of the bitmap */
  272.    max_tilex = (x+objp->image->width)/16 - tile_orgx;
  273.    min_tiley = (y-objp->image->height)/16 - tile_orgy;
  274.  
  275.    /* if you are trying to replace a tile beyond the screen borders, your
  276.       bitmap is out of range so just skip the whole thing */
  277.    if (tile_x < 0 || max_tilex > 21 || tile_y > 14 || min_tiley < 0)
  278.       return;
  279.  
  280.    /* set the layout array values to true, meaning the tile has been 
  281.       overwritten and must be redrawn next frame */
  282.    for (i = tile_x; i <= max_tilex; i++)
  283.    {
  284.       p = layout[hidden][i] + min_tiley;
  285.       for (j = min_tiley; j <= tile_y; j++)
  286.       {
  287.          *p++ = TRUE;
  288.       }
  289.    }
  290.  
  291.    /* now draw the bitmap */
  292.    put_sprite(objp->image,x,y);
  293. }
  294.  
  295. /**********************************************************************\
  296. *                                                                      *
  297. *  bullet_go                                                           *
  298. *  This is the bullet's main action function.  This function adjusts   *
  299. *  the x coordinate of the bullet (the y coordinate is constant) and   *
  300. *  tests if the bullet is off the screen.  It also handles collision   *
  301. *  detection with all the enemy fighters.  This function is for the    *
  302. *  player's bullets only.  Enemy bullets are handled in enemy_bullet_  *
  303. *  go.                                                                 *
  304. *                                                                      *
  305. \**********************************************************************/
  306.  
  307. void near bullet_go(OBJp objp)
  308. {
  309.    short max_x;
  310.    register short i;
  311.  
  312.    /* increment the bullet's horizontal position, always moves right */
  313.    objp->x += objp->xspeed;
  314.  
  315.    /* check if the bullet has moved off the screen */
  316.    max_x = (tile_orgx + objp->tile_xmax) * 16;
  317.  
  318.    /* if it has moved offf the screen, kill it by setting the action 
  319.       function to kill for the next frame */
  320.  
  321.    if (objp->x > max_x)
  322.       objp->action = &kill_bullet;
  323.  
  324.    /* Do collision detections for all the enemies in the enemy array.
  325.       This is a simple rectangular collision detection based on the
  326.       position, width and height of the enemy fighter. */
  327.    for (i = 0; i < nenemies; i++)
  328.    {
  329.       if (objp->x>enemy[i]->x  && objp->x<enemy[i]->x+enemy[i]->image->width && objp->y<enemy[i]->y && objp->y>enemy[i]->y-enemy[i]->image->height)
  330.       {
  331.  
  332.          /* if a collision is detected, make sure the enemy is not 
  333.             currently exploding */
  334.          if (enemy[i]->frame > 0)
  335.          {
  336.             /* spawn a new object, an explosion to attach to the enemy fighter */
  337.             start_explosion(enemy[i]);
  338.  
  339.             /* kill this bullet next frame */
  340.             objp->action = &kill_bullet;
  341.  
  342.             /* set the enemy frame to -1 to signal it is exploding and
  343.                can't explode again */
  344.             enemy[i]->frame = -1;
  345.  
  346.             /* stop doing collision detections because one bullet can't
  347.                blow up more than one plane */
  348.             break;
  349.          }
  350.       }
  351.    }
  352. }
  353.  
  354. /**********************************************************************\
  355. *                                                                      *
  356. *  do_explosion                                                        *
  357. *  This is the main action function for the explosion object.  Other   *
  358. *  functions affecting this object are start_explosion and kill_       *
  359. *  explosion. The attached sprite is the enemy fighter that is         *
  360. *  exploding.                                                          *
  361. *                                                                      *
  362. \**********************************************************************/
  363.  
  364. void near do_explosion(OBJp objp)
  365. {
  366.    /* If the explosion has reached the frame 3 state, at which point
  367.       the bitmap is bigger than the airplane, it is time to kill the
  368.       airplane. */
  369.  
  370.    if (objp->frame > 3)
  371.    {
  372.       /* if the attached sprite is NULL that means the airplane was
  373.          already killed so skip it.  Otherwise, kill the enemy plane
  374.          by setting it's action function to a kill function, and it
  375.          will kill itself next frame */
  376.       if (objp->attached_sprite != (OBJp)NULL)
  377.          objp->attached_sprite->action = &kill_enemy;
  378.  
  379.       /* after the attached plane has been killed, the explosion 
  380.          moves at a slower speed because smoke drifts slower than
  381.          metal */
  382.       objp->x += objp->xspeed;
  383.       objp->y += objp->yspeed;
  384.    }
  385.    else
  386.    {
  387.       /* if the enemy plane still exists, then the explosion should
  388.          be firmly attached to it.  Base the x and y coordinates of
  389.          the explosion on the position of the enemy.  Note, these
  390.          coordinates define the center of the explosion. */
  391.       if (objp->attached_sprite != (OBJp)NULL)
  392.       {
  393.          objp->x = objp->attached_sprite->x+16;
  394.          objp->y = objp->attached_sprite->y-4;
  395.       }
  396.  
  397.       /* it is possible for the explosion to be at less than frame 3
  398.          but there is no attached sprite.  That happens when the enemy
  399.          plane has drifted off the edge of the screen.  Just display
  400.          the sprite if you can */
  401.       else
  402.       {
  403.          objp->x += objp->xspeed;
  404.          objp->y += objp->yspeed;
  405.       }
  406.    }
  407.  
  408.    /* Increment the explosion frame only sometimes, and not at the
  409.       same speed for each explosion.  So base the frame increment on
  410.       the global random number */
  411.    if (random_number > 500 || objp->frame < 0)
  412.       objp->frame++;
  413.  
  414.    /* define which sprite will be displayed this frame */
  415.    objp->image = explosion[objp->frame];
  416.  
  417.    /* We only have 10 frames for this object.  If the frame has gone
  418.       over 10, it is time to kill the object.  Also be sure we do not
  419.       try to display an 11th frame, which will cause a null pointer */
  420.    if (objp->frame > 10)
  421.    {
  422.       objp->image = explosion[10];
  423.       objp->action = &kill_object;
  424.    }
  425. }
  426.  
  427. /**********************************************************************\
  428. *                                                                      *
  429. *  do_player_explosion                                                 *
  430. *  This function is similar to the do_explosion function except that   *
  431. *  this time it is the Quickfire plane that is hit, not the enemy      *
  432. *  plane. If this were a real game, after several hits (3 or 5)        *
  433. *  the player would die and you would need to restart the level or     *
  434. *  something.  However, this is just a demo game so we let Quickfire   *
  435. *  have unlimited lives.                                               *
  436. *                                                                      *
  437. \**********************************************************************/
  438.  
  439. void near do_player_explosion(OBJp objp)
  440. {
  441.    /* the explosion is firmly attached to the Quickfire plane */
  442.    objp->x += player->xspeed;
  443.    objp->y += player->yspeed;
  444.  
  445.    /* increment the explosion frame based on the random number */
  446.    if (random_number > 500 || objp->frame < 0)
  447.       objp->frame++;
  448.    objp->image = explosion[objp->frame];
  449.  
  450.    /* Stop the explosion at the third frame.  It's just a little
  451.       bitty explosion, after all. Save the big explosions for the
  452.       enemies */
  453.    if (objp->frame > 2)
  454.    {
  455.       objp->image = explosion[2];
  456.       objp->action = &kill_player_explosion;
  457.    }
  458. }
  459.  
  460. /**********************************************************************\
  461. *                                                                      *
  462. *  enemy_bullet_go                                                     *
  463. *  This is the main action function for the enemy bullets. The other   *
  464. *  functions affecting this object are start_enemy_bullet and kill_    *
  465. *  enemy_bullet.  This function adjusts the position of the bullet     *
  466. *  and does a collision detection.                                     *
  467. *                                                                      *
  468. \**********************************************************************/
  469.  
  470. void near enemy_bullet_go(OBJp objp)
  471. {
  472.    /* adjust the horizontal position of the bullet.  Speed is constant. */
  473.    objp->x += objp->xspeed;
  474.  
  475.    /* If the enemy bullet has traveled beyond the left edge of the screen,
  476.       it is time to kill it.  Just set its action function to a kill 
  477.       function and it will kill itself next frame. */
  478.    if (objp->x < tile_orgx*16)
  479.       objp->action = &kill_enemy_bullet;
  480.  
  481.    /* Do a collision detection with the player. */
  482.    else if (objp->x>player->x  && objp->x<player->x+player->image->width && objp->y<player->y && objp->y>player->y-player->image->height-3)
  483.    {
  484.       /* If a collision is detected, make sure the player is not currently
  485.          suffering a hit.  If the player already has an attached explosion,
  486.          skip this hit.  Otherwise, spawn an explosion. */
  487.       if (player->attached_sprite == (OBJp)NULL)
  488.          start_player_explosion(objp);
  489.  
  490.       /* either way, it's time to kill this bullet */
  491.       objp->action = &kill_enemy_bullet;
  492.    }
  493. }
  494.  
  495. /**********************************************************************\
  496. *                                                                      *
  497. *  enemy_go                                                            *
  498. *  This is the main action function for the enemy fighter. This        *
  499. *  function gets called once each frame for each fighter until the     *
  500. *  kill_enemy action function is called.  This function does two       *
  501. *  things: it updates the x and y coordinates of the figher, and if    *
  502. *  the player is within range, it may or may not fire a bullet at      *
  503. *  the player. It will not fire a bullet if it has been hit and        *
  504. *  is currently exploding.                                             *
  505. *                                                                      *
  506. \**********************************************************************/
  507.  
  508. void near enemy_go(OBJp objp)
  509. {
  510.    short ymin,ymax;
  511.    short dif;
  512.  
  513.    /* update the x and y positions */
  514.    objp->x += objp->xspeed;
  515.    objp->y += objp->yspeed;
  516.  
  517.    /* check if the fighter is going off the top or bottom of the screen */
  518.    ymin = tile_orgy*16 + objp->image->height;
  519.    ymax = tile_orgy*16 + 239;
  520.  
  521.    /* if going off the top or bottom of screen, change vertical direction */
  522.    if (objp->y <= ymin)
  523.    {
  524.       objp->yspeed = irandom(0,9);
  525.       objp->y = ymin;
  526.    }
  527.    else if (objp->y > ymax)
  528.    {
  529.       objp->yspeed = irandom(-9,0);
  530.       objp->y = ymax;
  531.    }
  532.  
  533.    /* if fighter has gone beyond the left edge of the screen, kill it */
  534.    else if (objp->x <= tile_orgx*16)
  535.       objp->action = &kill_enemy;
  536.  
  537.    /* Fighter is on the screen and it is not hit yet.  You can tell it
  538.       is not hit because it has no attached sprite.  That is, it is not
  539.       exploding. */
  540.    else if (objp->attached_sprite == (OBJp)NULL) /* not hit yet */
  541.    {
  542.       /* look at the difference between the player and the enemy */
  543.       dif = objp->y - player->y;
  544.  
  545.       /* Check the random number.  The enemy plane changes direction 
  546.          approximately once every 20 frames */
  547.       if (random_number < 50)
  548.          objp->yspeed = irandom(-9,9);
  549.  
  550.       /* If the player is within 64 pixels of the enemy, fire a bullet
  551.          approximately every 4th frame.  If you fire a bullet, get a
  552.          new random number because we don't want all the enemies firing
  553.          at the same time. */
  554.       else if (random_number % 4 == 0 && dif > -64 && dif < 64)
  555.       {
  556.          start_enemy_bullet(objp);
  557.          random_number = irandom(0,1000);
  558.       }
  559.    }
  560. }
  561.  
  562. /**********************************************************************\
  563. *                                                                      *
  564. *  kill_bullet                                                         *
  565. *  This is where the player's bullet kills itself.  It just removes    *
  566. *  itself from the linked list.                                        *
  567. *                                                                      *
  568. \**********************************************************************/
  569.  
  570. void near kill_bullet(OBJp objp)
  571. {
  572.    OBJp node;
  573.  
  574.    /* remove object from linked list */
  575.    kill_object(objp);
  576.  
  577.    /* decrement the number of bullets to keep the count accurate */
  578.    nbullets--;
  579. }
  580.  
  581. /**********************************************************************\
  582. *                                                                      *
  583. *  kill_enemy                                                          *
  584. *  This is where the enemy fighter removes itself from the linked list *
  585. *  and also from the enemy array.                                      *
  586. *                                                                      *
  587. \**********************************************************************/
  588.  
  589. void near kill_enemy(OBJp objp)
  590. {
  591.    OBJp node;
  592.    register int i;
  593.    int enemy_no;
  594.  
  595.    if (node->attached_sprite != (OBJp)NULL)
  596.       node->attached_sprite->attached_sprite = (OBJp)NULL;
  597.  
  598.    /* which enemy is it? */
  599.    
  600.    for (i = 0; i < nenemies; i++)
  601.    {
  602.       if (enemy[i] == objp) 
  603.       {
  604.          enemy_no = i;
  605.          break;
  606.       }
  607.    }
  608.  
  609.    /* decrement the enemy count */
  610.  
  611.    nenemies--;
  612.  
  613.    /* the other enemies fill in the space in the enemy array vacated 
  614.       by this enemy */
  615.  
  616.    for (i = enemy_no; i < nenemies; i++)
  617.       enemy[i] = enemy[i+1];
  618.    enemy[nenemies] = (OBJp)NULL;
  619.  
  620.    /* remove from linked list */
  621.    kill_object(objp);
  622. }
  623.  
  624. /**********************************************************************\
  625. *                                                                      *
  626. *  kill_enemy_bullet                                                   *
  627. *  This is the action function where the enemy bullet kills itself     *
  628. *  by removing itself from the linked list.                            *
  629. *                                                                      *
  630. \**********************************************************************/
  631.  
  632. void near kill_enemy_bullet(OBJp objp)
  633. {
  634.    /* remove object from linked list */
  635.    kill_object(objp);
  636.  
  637.    /* decrement number of enemy bullets on the screen */
  638.    nenemy_bullets--;
  639. }
  640.  
  641. /**********************************************************************\
  642. *                                                                      *
  643. *  kill_object                                                         *
  644. *  Remove an object from a linked list                                 *
  645. *                                                                      *
  646. \**********************************************************************/
  647.  
  648. void near kill_object(OBJp objp)
  649. {
  650.    OBJp node;
  651.  
  652.    node = objp;
  653.    if (node == bottom_node)
  654.    {
  655.       bottom_node = node->next;
  656.       if (bottom_node != (OBJp)NULL)
  657.          bottom_node->prev = (OBJp)NULL;
  658.    }
  659.    else if (node == top_node)
  660.    {
  661.       top_node = node->prev;
  662.       top_node->next = (OBJp)NULL;
  663.    }
  664.    else
  665.    {
  666.       node->prev->next = node->next;
  667.       node->next->prev = node->prev;
  668.    }
  669.    free(node);
  670. }
  671.  
  672. /**********************************************************************\
  673. *                                                                      *
  674. *  kill_player_explosion                                               *
  675. *  The little explosion on the Quickfire plane kills itself by         *
  676. *  removing itself from the linked list.  Note, the player plane       *
  677. *  is never killed, just the explosion attached to it.                 *
  678. *                                                                      *
  679. \**********************************************************************/
  680.  
  681. void near kill_player_explosion(OBJp objp)
  682. {
  683.    /* remove object from linked list */
  684.    kill_object(objp);
  685.  
  686.    /* The player now has no attached sprite so it is eligible for
  687.       another explosion.  */
  688.    player->attached_sprite = (OBJp)NULL;
  689. }
  690.  
  691. /**********************************************************************\
  692. *                                                                      *
  693. *  launch_sequence -- with palette cycling                             *
  694. *  The Quickfire fighter does a roll then locks into position in the   *
  695. *  launch shoot. It proceeds down the launch shoot with some palette   *
  696. *  cycling to cause a propulsion effect. Meanwhile the computer is     *
  697. *  being benchmarked informally. How fast Quickfire exits the          *
  698. *  launch shoot will determine how fast Quickfire does rolls later on. *
  699. *                                                                      *
  700. \**********************************************************************/
  701.  
  702. void near launch_sequence()
  703. {
  704.    register short i,j;
  705.    unsigned long time1, time2;
  706.    long nframes;
  707.  
  708.    scrolled = FALSE;
  709.    player->y = 94;
  710.    player->frame = 2;
  711.    player->image = fighter[player->frame];
  712.    apply_sprite(player);
  713.    swap();
  714.  
  715.    /* begin benchmark */
  716.    time1 = fg_getclock();
  717.    nframes = 0;
  718.  
  719.    /* roll fighter shorto position */
  720.    for (i = 0; i < 2; i++)
  721.    {
  722.       for (j = 32*3; j > 2; j-=3)
  723.       {
  724.          nframes++;
  725.          if (nframes % 8 == 0)
  726.          {
  727.             player->frame++;
  728.             if (player->frame > 7)
  729.                player->frame = 0;
  730.             player->image = fighter[player->frame];
  731.          }
  732.          player->y+=2;
  733.          rebuild_hidden();
  734.          apply_sprite(player);
  735.          swap();
  736.          fg_setdacs(64,32,&launch_palette[j]);
  737.          if (fg_kbtest(KB_ESC)) terminate_game();
  738.       }
  739.    }
  740.  
  741.    player->image = fighter[2];
  742.  
  743.    /* begin proceeding through launch shoot */
  744.    for (i = 0; i < 1; i++)
  745.    {
  746.       for (j = 32*3; j > 2; j-=3)
  747.       {
  748.          nframes++;
  749.          player->x++;
  750.          rebuild_hidden();
  751.          apply_sprite(player);
  752.          swap();
  753.          fg_setdacs(64,32,&launch_palette[j]);
  754.          if (scrolled)
  755.          {
  756.             page_copy();
  757.             scrolled = FALSE;
  758.          }
  759.          if (fg_kbtest(KB_ESC)) terminate_game();
  760.       }
  761.    }
  762.    for (i = 0; i < 5; i++)
  763.    {
  764.       for (j = 32*3; j > 11; j-=12)
  765.       {
  766.          nframes++;
  767.          player->x+=4;
  768.          scroll_right(4);
  769.          rebuild_hidden();
  770.          apply_sprite(player);
  771.          swap();
  772.          fg_setdacs(64,32,&launch_palette[j]);
  773.          if (scrolled)
  774.          {
  775.             page_copy();
  776.             scrolled = FALSE;
  777.          }
  778.          if (fg_kbtest(KB_ESC)) terminate_game();
  779.       }
  780.    }
  781.    for (i = 0; i < 2; i++)
  782.    {
  783.       for (j = 32*3; j > 15; j-=15)
  784.       {
  785.          nframes++;
  786.          player->x+=5;
  787.          scroll_right(4);
  788.          rebuild_hidden();
  789.          apply_sprite(player);
  790.          swap();
  791.          fg_setdacs(64,32,&launch_palette[j]);
  792.          if (scrolled)
  793.          {
  794.             page_copy();
  795.             scrolled = FALSE;
  796.          }
  797.          if (fg_kbtest(KB_ESC)) terminate_game();
  798.       }
  799.    }
  800.    for (i = 0; i < 3; i++)
  801.    {
  802.       for (j = 32*3; j > 15; j-=15)
  803.       {
  804.          nframes++;
  805.          player->x+=8;
  806.          scroll_right(8);
  807.          rebuild_hidden();
  808.          apply_sprite(player);
  809.          swap();
  810.          fg_setdacs(64,32,&launch_palette[j]);
  811.          if (scrolled)
  812.          {
  813.             page_copy();
  814.             scrolled = FALSE;
  815.          }
  816.          if (fg_kbtest(KB_ESC)) terminate_game();
  817.       }
  818.    }
  819.    for (i = 0; i < 4; i++)
  820.    {
  821.       for (j = 32*3; j > 15; j-=15)
  822.       {
  823.          nframes++;
  824.          player->x+=12;
  825.          scroll_right(12);
  826.          rebuild_hidden();
  827.          apply_sprite(player);
  828.          swap();
  829.          fg_setdacs(64,32,&launch_palette[j]);
  830.          if (scrolled)
  831.          {
  832.             page_copy();
  833.             scrolled = FALSE;
  834.          }
  835.          if (fg_kbtest(KB_ESC)) terminate_game();
  836.       }
  837.    }
  838.  
  839.    /* exit launch shoot */
  840.    for (i = 0; i < 21; i++)
  841.    {
  842.       nframes++;
  843.       player->x+=12;
  844.       scroll_right(8);
  845.       rebuild_hidden();
  846.       apply_sprite(player);
  847.       swap();
  848.       if (scrolled)
  849.       {
  850.          page_copy();
  851.          scrolled = FALSE;
  852.       }
  853.       if (fg_kbtest(KB_ESC)) terminate_game();
  854.    }
  855.  
  856.    /* 1 frame so roll fighter plane into position */
  857.    player->image = fighter[1];
  858.  
  859.    /* figure out the frame rate from the benchmark for later animation timing */
  860.    time2 = fg_getclock();
  861.    time1 = ((time2-time1) * 10) / 182;
  862.    if (time1 > 0)
  863.       framerate = (short)(nframes/time1);
  864.    frame_factor = framerate/40 + 1;
  865.    if (frame_factor < 1) frame_factor = 1;
  866. }
  867.  
  868. /**********************************************************************\
  869. *                                                                      *
  870. *  new_score                                                           *
  871. *  This is the action function for the score object.  The action       *
  872. *  function only gets activated when the score gets changed. The       *
  873. *  rest of the time the score's action function is put_score. For      *
  874. *  speed, the numbers are drawn only once and then the whole score     *
  875. *  is stored in a bitmap so it can be redisplayed every frame until    *
  876. *  it changes.                                                         *
  877. *                                                                      *
  878. \**********************************************************************/
  879.  
  880. void near new_score(OBJp objp)
  881. {
  882.    short nchar;
  883.    char string[16];
  884.  
  885.    /* Convert the (long) score to a character string.  Assume 10 digits
  886.       is enough */
  887.    ltoa(player_score,string,10);
  888.  
  889.    /* calculate the length of the string */
  890.    nchar = strlen(string);
  891.  
  892.    /* clear an area in video memory below the tile space where nothing 
  893.       else is going on */
  894.    fg_setcolor(0);
  895.    fg_rect(0,59,680,684);
  896.  
  897.    /* set the color to white and display the score */
  898.    fg_setcolor(255);
  899.    put_bstring(string,nchar,0,684);
  900.  
  901.    /* the width of the bitmap will depend on the number of characters */
  902.    objp->image->width = nchar*6;
  903.  
  904.    /* do an fg_getimage to put the score in a bitmap in RAM */
  905.    fg_move(0,684);
  906.    fg_getimage(objp->image->bitmap,objp->image->width,objp->image->height);
  907.  
  908.    /* update the x and y positions of the score (always the same place
  909.       in the upper left corner) */
  910.    objp->x = tile_orgx*16 + screen_orgx + 8;
  911.    objp->y = tile_orgy*16 + screen_orgy + 10;
  912.  
  913.    /* the default action function for the score object is put score */
  914.    objp->action = &put_score;
  915. }
  916.  
  917. /**********************************************************************\
  918. *                                                                      *
  919. *  page_copy                                                           *
  920. *  Do a full-screen copy from the visual page to the hidden page.      *
  921. *  Usually this is done after a scroll. Also copy the layout array.    *
  922. *                                                                      *
  923. \**********************************************************************/
  924.  
  925. void near page_copy()
  926. {
  927.    /* copy the visual page to the hidden page */
  928. #ifdef ModeX
  929.    fg_transfer(0,351,vpo,vpb,0,hpb,0,0);
  930. #else
  931.    fg_vbcopy(0,351,vpo,vpb,0,hpb,workvb,workvb);
  932. #endif
  933.  
  934.    /* also copy the layout */
  935.    memcpy(layout[hidden],layout[visual],22*15);
  936. }
  937.  
  938. /**********************************************************************\
  939. *                                                                      *
  940. *  player_go                                                           *
  941. *  This is the only action function for the Quickfire plane, also      *
  942. *  known as the player.  A lot of things happen in this function,      *
  943. *  including the artificial intelligence required when the game        *
  944. *  goes into demo mode. All keystrokes are intercepted in this         *
  945. *  function.                                                           *
  946. *                                                                      *
  947. \**********************************************************************/
  948.  
  949. void near player_go()
  950. {
  951.    short tile_x,tile_y;
  952.    short scroll_y;
  953.    OBJp node;
  954.    short keystroke;
  955.    short dif;
  956.  
  957.    /* initialize some useful constants */
  958.    scroll_y = 0;
  959.    player->xspeed = 5;
  960.  
  961.    /* calculate the current position of the player in tile space */
  962.    tile_x = player->x/16 - tile_orgx;
  963.  
  964.    /* Increment the timer.  If the timer exceeds the target amount, it
  965.       is time to change direction of the airplane.  We also assume at
  966.       this point that the program has gone into demo mode ("autopilot").
  967.       In other words, if the keyboard has not been touched in n frames,
  968.       let the program run itself. */
  969.    player_timer++;
  970.    if (player_timer >= player_time_target)
  971.    {
  972.       autopilot = TRUE;
  973.       player->yspeed = 0;
  974.       player->xspeed = 5;
  975.  
  976.       /* Since we haven't collected a keystroke in a while, we will
  977.          make one up.  In fact, we will make up several depending on
  978.          a random value.  We assume there are 16 cases.  These cases
  979.          can be manipulated to make Quickfire respond differently in
  980.          demo mode. */
  981.       up = 0; down = 0; right = 0; left = 0; ctrl = 0;
  982.       keystroke = irandom(0,16);
  983.       switch(keystroke)
  984.       {
  985.         case 0:
  986.           up    = TRUE; break;
  987.         case 1:
  988.           down  = TRUE; break;
  989.         case 2:
  990.           right = TRUE; break;
  991.         case 3:
  992.           left  = TRUE; break;
  993.         case 4:
  994.           ctrl  = TRUE; break;
  995.  
  996.         case 5:
  997.           left  = TRUE; down = TRUE; break;
  998.         case 6:
  999.           left  = TRUE; up   = TRUE; break;
  1000.         case 7:
  1001.           right = TRUE; up   = TRUE; break;
  1002.         case 8:
  1003.           right = TRUE; down = TRUE; break;
  1004.  
  1005.         case 9:
  1006.           up   = TRUE; left  = TRUE; ctrl = TRUE; break;
  1007.         case 10:
  1008.           down = TRUE; right = TRUE; ctrl = TRUE; break;
  1009.         case 11:
  1010.           left = TRUE; ctrl  = TRUE;              break;
  1011.         case 12:
  1012.           down = TRUE; left  = TRUE; ctrl = TRUE; break;
  1013.  
  1014.         case 13:
  1015.           up   = TRUE; left  = TRUE; ctrl = TRUE; break;
  1016.         case 14:
  1017.           down = TRUE; left  = TRUE; ctrl = TRUE; break;
  1018.         case 15:
  1019.           up   = TRUE; right = TRUE; ctrl = TRUE; break;
  1020.         case 16:
  1021.           down = TRUE; right = TRUE; ctrl = TRUE; break;
  1022.  
  1023.       }
  1024.       player_timer = 0;
  1025.       player_time_target = irandom(25,80);
  1026.    }
  1027.  
  1028.    /* one last chance... is a key being pressed?  If it is, ignore all
  1029.       that stuff above and let the player override the autopilot */
  1030.    if (fg_kbtest(0))
  1031.    {
  1032.       up = 0; down = 0; right = 0; left = 0; ctrl = 0; player_timer=0; autopilot = FALSE;
  1033.    }
  1034.  
  1035.    /* If we are in autopilot mode, let's make some decisions regarding
  1036.       motion and firing bullets. */
  1037.    if (autopilot)
  1038.    {
  1039.       /* if there is an enemy on the screen, maybe we want to shoot at it. */
  1040.       if (enemy[0] != (OBJp)NULL)
  1041.       {
  1042.          if (nbullets == 0) 
  1043.             frame_count = 0;
  1044.  
  1045.          /* setting ctrl and left at the same time will cause a roll */
  1046.          right = FALSE;
  1047.          ctrl = TRUE;
  1048.          left = TRUE;
  1049.  
  1050.          /* get the vertical difference between the player and the
  1051.             closest enemy */
  1052.          dif = player->y - enemy[0]->y;
  1053.  
  1054.          /* If the closest enemy is below us, change direction to
  1055.             down.  If he is above us, go up. */
  1056.  
  1057.          if (dif > 0) 
  1058.             up = TRUE;
  1059.          else
  1060.             down = TRUE;
  1061.  
  1062.          /* reset the time target */
  1063.          player_time_target = 12;
  1064.  
  1065.          /* sometimes, for variety, don't roll */
  1066.          if (random_number > 500)
  1067.             left = FALSE;
  1068.       }
  1069.    else ctrl = 0;
  1070.    }
  1071.  
  1072.    /* case where Quickfire is going up */
  1073.    if (fg_kbtest(KB_UP) || up)
  1074.    {
  1075.       /* Start the roll by changing the sprite.  Divide by the frame
  1076.          factor.  On a slow computer, the frame factor will be 1 so
  1077.          change the sprite every frame.  On a faster computer, change
  1078.          the sprite after 2 or 3 frames, otherwise the plane rolls too
  1079.          fast */
  1080.       frame_count++;
  1081.       if (frame_count < 8*frame_factor)
  1082.          player->frame = frame_count/frame_factor;
  1083.       else 
  1084.          player->frame = 0;
  1085.  
  1086.       /* if you are getting close to the top of the screen, only decrement
  1087.          2 pixels per frame, otherwise go ahead and climb 4 pixels per 
  1088.          frame */
  1089.       if (player->y > 64 )
  1090.       {
  1091.          if(player->yspeed > -4)
  1092.            player->yspeed--;
  1093.       }
  1094.       else
  1095.          player->yspeed = -2;
  1096.        
  1097.    }
  1098.  
  1099.    /* case where Quickfire is going down */
  1100.    else if (fg_kbtest(KB_DOWN) || down)
  1101.    {
  1102.       frame_count++;
  1103.       if (frame_count < 8*frame_factor)
  1104.          player->frame = frame_count/frame_factor;
  1105.       else 
  1106.          player->frame = 0;
  1107.       if (frame_count > 32000)
  1108.          frame_count = 0;
  1109.  
  1110.       /* if you are getting close to the bottom of the screen, only decrement
  1111.          2 pixels per frame, otherwise go ahead and descend 4 pixels per 
  1112.          frame */
  1113.       if (player->y < world_maxy - 32)
  1114.       {
  1115.         if (player->yspeed < 4)
  1116.            player->yspeed++;
  1117.       }
  1118.       else
  1119.          player->yspeed = 2;
  1120.    }
  1121.  
  1122.    /* case where Quickfire is going right */
  1123.    if (fg_kbtest(KB_RIGHT) || right)
  1124.    {
  1125.       /* if you are at the far right edge of the screen, just travel at
  1126.          the same rate as the auto scroll */
  1127.       if (tile_x >= player->tile_xmax)
  1128.          player->xspeed = 8;
  1129.  
  1130.       /* elsewhere on the screen you can go right faster than the 
  1131.          autoscroll */
  1132.       else 
  1133.          player->xspeed = 15;
  1134.    }
  1135.  
  1136.  
  1137.    /* if the left arrow key is pressed, Quickfire slows way down */
  1138.    else if (fg_kbtest(KB_LEFT) || left)
  1139.    {
  1140.       player->xspeed = 1;
  1141.    }
  1142.  
  1143.    /* nothing happening, Quickfire should be in upright position */
  1144.    else
  1145.    {
  1146.       frame_count = 0;
  1147.       player->frame = 0;
  1148.    }
  1149.  
  1150.    /* decide which sprite will be displayed this frame */
  1151.    player->image = fighter[player->frame];
  1152.  
  1153.    /* Quickfire can't go past far left side of screen */
  1154.    if (tile_x < player->tile_xmin)
  1155.       player->xspeed = MAX(player->xspeed,8);
  1156.  
  1157.    /* increment x coordinate according to speed of player */
  1158.    player->x += player->xspeed;
  1159.  
  1160.    /* also adjust the y coordinate, keeping plane within world limits */
  1161.    player->y += player->yspeed;
  1162.    player->y = MIN(player->y,world_maxy-3);
  1163.    player->y = MAX(player->y,32);
  1164.  
  1165.    /* calculate y position in tile space */
  1166.    tile_y = player->y/16 - tile_orgy;
  1167.  
  1168.    /* are we going to need to scroll up or down? */
  1169.    if (tile_y > player->tile_ymax && (tile_orgy < nrows - 15 || screen_orgy <= 36))
  1170.       scroll_y = player->yspeed;
  1171.    else if (tile_y < player->tile_ymin && (tile_orgy > 0 || screen_orgy > 4))
  1172.       scroll_y = player->yspeed;
  1173.  
  1174.    /* Try to do the scroll.  If the scroll fails it is because you have
  1175.       come to the end of the map and you need to adjust everything for the
  1176.       wrap around.  Adjust the x coordinates of all the objects in the
  1177.       linked list, and also adjust the tile origin. */
  1178.    if (scroll_right_down(scroll_y) == -1)
  1179.    {
  1180.       player->x = (player->x - (tile_orgx*16)) + (54*16) - player->xspeed;
  1181.       for (node=bottom_node; node!=(OBJp)NULL; node=node->next)
  1182.          node->x = (node->x - (tile_orgx*16)) + (54*16) - node->xspeed;
  1183.       tile_orgx = 54;
  1184.    }
  1185.  
  1186.    /* If you need to shoot this frame, spawn a bullet.  Don't spawn 
  1187.       more than 8 bullets at a time. */
  1188.    if (fg_kbtest(KB_CTRL) || ctrl)
  1189.    {
  1190.       bullet_count++;
  1191.       player_timer=0;
  1192.       start_bullet();
  1193.       if (bullet_count >= 8)
  1194.       {
  1195.          bullet_count = 0;
  1196.          ctrl = FALSE;
  1197.          autopilot = FALSE;
  1198.       }
  1199.    }
  1200. }
  1201.  
  1202. /**********************************************************************\
  1203. *                                                                      *
  1204. *  put_sprite                                                          *
  1205. *  This function is called each frame for each sprite currently        *
  1206. *  visible.  The world_x and world_y coordinates refer to the location *
  1207. *  of the sprite on the greater "map", or in other words, it is the    *
  1208. *  object's position with respect to the sky and the clouds.           *
  1209. *                                                                      *
  1210. \**********************************************************************/
  1211.  
  1212. void near put_sprite(SPRITE *frame, short world_x, short world_y)
  1213. {
  1214.    register short x,y;
  1215.  
  1216.    /* convert x and y world space to x and y physical space */
  1217.    x = world_x - (tile_orgx*16);
  1218.    y = world_y - (tile_orgy*16) + hpo;
  1219.  
  1220.    /* draw the bitmap at the proper location */
  1221.    fg_move(x,y);
  1222.    fg_clpimage(frame->bitmap,frame->width,frame->height);
  1223. }
  1224.  
  1225. /**********************************************************************\
  1226. *                                                                      *
  1227. *  put_tile                                                            *
  1228. *  apply tile to screen or buffer                                      *
  1229. *                                                                      *
  1230. \**********************************************************************/
  1231.  
  1232. void put_tile(short i,short j)
  1233. {
  1234.    short x1,x2,x3,y1,y2,y3;
  1235.    short tileno;
  1236.    unsigned short index;
  1237.  
  1238.    tileno = (short)backtile[i+tile_orgx][j+tile_orgy];
  1239.    x1 = (tileno%20) * 16;
  1240.    x2 = x1 + 15;
  1241.  
  1242.    y1 = (tileno/20) * 16 + tpo;
  1243.    y2 = y1 + 15;
  1244.  
  1245.    x3 = i * 16;
  1246.    y3 = j * 16+hpo +15;
  1247. #ifdef ModeX
  1248.    fg_transfer(x1,x2,y1,y2,x3,y3,0,0);
  1249. #else
  1250.    fg_vbcopy(x1,x2,y1,y2,x3,y3,workvb,workvb);
  1251. #endif
  1252.  
  1253. }
  1254.  
  1255. /**********************************************************************\
  1256. *                                                                      *
  1257. *  rebuild_hidden                                                      *
  1258. *  This function is called exactly once each frame. It's purpose is    *
  1259. *  to redraw the screen on the hidden page by replacing only those     *
  1260. *  tiles which have changed. Since all sprites on the screen are       *
  1261. *  assumed to be constantly moving, we replace all tiles that were     *
  1262. *  covered by sprites on the previous frame.                           *
  1263. *                                                                      *
  1264. \**********************************************************************/
  1265.  
  1266. void near rebuild_hidden()
  1267. {
  1268.    register short i,j;
  1269.    char *p;
  1270.  
  1271.    /* here is where the layout array performs its magic */
  1272.    p = layout[hidden][0];
  1273.  
  1274.    /* Traverse the layout array and every time you find a tile that
  1275.       has changed, replace it.  After the tile is replaced, naturally
  1276.       that element in the array is set to 0 or "FALSE". */
  1277.    for (i = 0; i < 22; i++)
  1278.    {
  1279.       for (j = 0; j < 15; j++)
  1280.       {
  1281.          if (*p)
  1282.          {
  1283.             put_tile(i,j);
  1284.             *p = FALSE;
  1285.          }
  1286.          p++;
  1287.       }
  1288.    }
  1289. }
  1290.  
  1291. /**********************************************************************\
  1292. *                                                                      *
  1293. *  put_score -- put score on the screen                                *
  1294. *  This is the simpler score action function that gets called every    *
  1295. *  frame the score has not changed.  All it does is calculate the x    *
  1296. *  and y coordinates of the score based on the origin of the visual    *
  1297. *  screen.                                                             *
  1298. *                                                                      *
  1299. \**********************************************************************/
  1300.  
  1301. void near put_score(OBJp objp)
  1302. {
  1303.    objp->x = tile_orgx*16 + screen_orgx + 8;
  1304.    objp->y = tile_orgy*16 + screen_orgy + 10;
  1305. }
  1306.  
  1307. /**********************************************************************\
  1308. *                                                                      *
  1309. *  scroll_right                                                        *
  1310. *  Move the screen to the right by npixels. Quickfire automatically    *
  1311. *  scrolls right every frame, but sometimes it scrolls right and up,   *
  1312. *  and sometimes it scrolls right and down.                            *
  1313. *                                                                      *
  1314. \**********************************************************************/
  1315.  
  1316. int scroll_right(short npixels)
  1317. {
  1318.    register short i;
  1319.  
  1320.    /* if possible, just move the visible area within the physical page */
  1321.    if (screen_orgx <= 32-npixels)
  1322.    {
  1323.       screen_orgx+=npixels;
  1324.    }
  1325.  
  1326.    /* the origin is out of range, we need to do a full scroll */
  1327.    else if (tile_orgx < ncols - 22)
  1328.    {
  1329.       /* transfer the relevant part of the visual page to the hidden page */
  1330. #ifdef ModeX
  1331.       fg_transfer(32,351,vpo,vpb,0,hpb,0,0);
  1332. #else
  1333.       fg_vbcopy(32,351,vpo,vpb,0,hpb,workvb,workvb);
  1334. #endif
  1335.  
  1336.       /* change the origin in tile space and screen space */
  1337.       tile_orgx+=2;
  1338.       screen_orgx-=(32-npixels);
  1339.  
  1340.       /* fill in two columns of tiles at the right side of the page */
  1341.       for(i = 0; i < 15; i++)
  1342.       {
  1343.          put_tile(21,i);
  1344.          put_tile(20,i);
  1345.       }
  1346.  
  1347.       /* fix that layout array */
  1348.       adjust_layout_right();
  1349.  
  1350.       /* set a global to signal a page copy later in the frame */
  1351.       scrolled = TRUE;
  1352.    }
  1353.    else /* can't scroll right */
  1354.    {
  1355.       return(-1);
  1356.    }
  1357.    return(OK);
  1358. }
  1359.  
  1360. /**********************************************************************\
  1361. *                                                                      *
  1362. *  scroll_right_down                                                   *
  1363. *                                                                      *
  1364. *  Master scrolling function. Assumes you are going to scroll right    *
  1365. *  and down.  If you are going to scroll right and up or just right,   *
  1366. *  it branches to other functions.                                     *
  1367. *                                                                      *
  1368. *  Scrolling right and down, there are 4 cases:                        *
  1369. *                                                                      *
  1370. *    - scroll by adjusting screen coordinates only                     *
  1371. *    - adjust x screen coordinates and y tiles                         *
  1372. *    - adjust x tiles and y screen coordinates                         *
  1373. *    - adjust x and y tiles                                            *
  1374. *                                                                      *
  1375. *  The last case is the most time-consuming because you must replace   *
  1376. *  a total of 52 tiles, that is, 2 columns of 15 and one row of 22     *
  1377. *  tiles.                                                              *
  1378. *                                                                      *
  1379. \**********************************************************************/
  1380.  
  1381. int scroll_right_down(short scroll_y)
  1382. {
  1383.    register short i;
  1384.    short scroll_x;
  1385.  
  1386.    /* Scroll_x is a constant.  We always scroll 8 pixels in Quickfire. */
  1387.    scroll_x = 8; 
  1388.  
  1389.    /* if you are not scrolling up or down, just scroll right */
  1390.    if (scroll_y == 0)
  1391.       return(scroll_right(scroll_x));
  1392.  
  1393.    /* if you are scrolling up, go do that */
  1394.    else if (scroll_y < 0)
  1395.       return(scroll_right_up(scroll_x,scroll_y));
  1396.  
  1397.    /* can we get away with just changing the screen origin? */
  1398.    else if (screen_orgx <= 32-scroll_x && screen_orgy <= 40-scroll_y)
  1399.    {
  1400.       screen_orgx+=scroll_x;
  1401.       screen_orgy+=scroll_y;
  1402.    }
  1403.  
  1404.    /* You have reached the end of the world map.  Can't scroll, will
  1405.       have to wrap around.  Return an error code.  */
  1406.    else if (tile_orgx >= ncols - 22)
  1407.    {
  1408.       return(-1);
  1409.    }
  1410.  
  1411.    /* you are at the bottom of the screen, can't scroll down, so 
  1412.       just scroll right */
  1413.    else if (tile_orgy >= nrows - 15)
  1414.    {
  1415.       return(scroll_right(scroll_x));
  1416.    }
  1417.  
  1418.    /* we are going to need to adjust the screen by 2 columns of tiles */
  1419.    else if (screen_orgx > 32-scroll_x)
  1420.    {
  1421.       /* but this time we don't have to adjust any rows */
  1422.       if (screen_orgy <= 40-scroll_y)
  1423.       {
  1424. #ifdef ModeX
  1425.          fg_transfer(32,351,vpo,vpb,0,hpb,0,0);
  1426. #else
  1427.          fg_vbcopy(32,351,vpo,vpb,0,hpb,workvb,workvb);
  1428. #endif
  1429.          tile_orgx+=2;
  1430.          screen_orgx-=(32-scroll_x);
  1431.          screen_orgy+=scroll_y;
  1432.          for(i = 0; i < 15; i++)
  1433.          {
  1434.             put_tile(21,i);
  1435.             put_tile(20,i);
  1436.          }
  1437.          adjust_layout_right();
  1438.          scrolled = TRUE;
  1439.       }
  1440.  
  1441.       /* here we will adjust 2 columns and one row of tiles (slowest case) */
  1442.       else
  1443.       {
  1444. #ifdef ModeX
  1445.          fg_transfer(32,351,16+vpo,vpb,0,223+hpo,0,0);
  1446. #else
  1447.          fg_vbcopy(32,351,16+vpo,vpb,0,223+hpo,workvb,workvb);
  1448. #endif
  1449.          tile_orgx+=2;
  1450.          tile_orgy++;
  1451.          screen_orgx-=(32-scroll_x);
  1452.          screen_orgy-=(16-scroll_y);
  1453.          for(i = 0; i < 15; i++)
  1454.          {
  1455.             put_tile(21,i);
  1456.             put_tile(20,i);
  1457.          }
  1458.          for(i = 0; i < 22; i++)
  1459.             put_tile(i,14);
  1460.  
  1461.          for (i = 0; i < 20; i++)
  1462.             memcpy(layout[hidden][i],&layout[visual][i+2][1],14); 
  1463.  
  1464.          scrolled = TRUE;
  1465.       }
  1466.    }
  1467.  
  1468.    /* in this case we only have to adjust one row of tiles, columns
  1469.       stay the same */
  1470.    else 
  1471.    {
  1472. #ifdef ModeX
  1473.       fg_transfer(0,351,16+vpo,vpb,0,223+hpo,0,0);
  1474. #else
  1475.       fg_vbcopy(0,351,16+vpo,vpb,0,223+hpo,workvb,workvb);
  1476. #endif
  1477.       tile_orgy++;
  1478.       screen_orgx+=scroll_x;
  1479.       screen_orgy-=(16-scroll_y);
  1480.       for(i = 0; i < 22; i++)
  1481.       {
  1482.          put_tile(i,14);
  1483.       }
  1484.       adjust_layout_down();
  1485.       scrolled = TRUE;
  1486.    }
  1487.    return(OK);
  1488. }
  1489.  
  1490. /**********************************************************************\
  1491. *                                                                      *
  1492. *  scroll_right_up                                                     *
  1493. *  This function works exactly the same as scroll_right_down, except   *
  1494. *  it scrolls up instead of down.                                      *
  1495. *                                                                      *
  1496. \**********************************************************************/
  1497.  
  1498. int scroll_right_up(short scroll_x,short scroll_y)
  1499. {
  1500.    register short i;
  1501.  
  1502.    /* case where only screen origin coordinates need to be changed */
  1503.    if (screen_orgx <= 32-scroll_x && screen_orgy >= -scroll_y)
  1504.    {
  1505.       screen_orgx+=scroll_x;
  1506.       screen_orgy+=scroll_y;
  1507.    }
  1508.  
  1509.    /* can't scroll, need to wrap around */
  1510.    else if (tile_orgx >= ncols - 22)
  1511.    {
  1512.       return(-1);
  1513.    }
  1514.  
  1515.    /* top of map, scroll right only */
  1516.    else if (tile_orgy <= 0)
  1517.    {
  1518.       return(scroll_right(scroll_x));
  1519.    }
  1520.  
  1521.    /* case where columns only need to be replaced */
  1522.    else if (screen_orgx > 32-scroll_x)
  1523.    {
  1524.       if (screen_orgy >= -scroll_y)
  1525.       {
  1526. #ifdef ModeX
  1527.          fg_transfer(32,351,vpo,vpb,0,hpb,0,0);
  1528. #else
  1529.          fg_vbcopy(32,351,vpo,vpb,0,hpb,workvb,workvb);
  1530. #endif
  1531.          tile_orgx+=2;
  1532.          screen_orgx-=(32-scroll_x);
  1533.          screen_orgy+=scroll_y;
  1534.          for(i = 0; i < 15; i++)
  1535.          {
  1536.             put_tile(21,i);
  1537.             put_tile(20,i);
  1538.          }
  1539.          adjust_layout_right();
  1540.          scrolled = TRUE;
  1541.       }
  1542.  
  1543.       /* must replace 2 columns and one row of tiles */
  1544.       else
  1545.       {
  1546. #ifdef ModeX
  1547.          fg_transfer(32,351,vpo,223+vpo,0,hpb,0,0);
  1548. #else
  1549.          fg_vbcopy(32,351,vpo,223+vpo,0,hpb,workvb,workvb);
  1550. #endif
  1551.          tile_orgx+=2;
  1552.          tile_orgy--;
  1553.          screen_orgx-=(32-scroll_x);
  1554.          screen_orgy+=(16+scroll_y);
  1555.          for(i = 0; i < 15; i++)
  1556.          {
  1557.             put_tile(21,i);
  1558.             put_tile(20,i);
  1559.          }
  1560.          for(i = 0; i < 22; i++)
  1561.             put_tile(i,0);
  1562.  
  1563.          for (i = 0; i < 20; i++)
  1564.             memcpy(&layout[hidden][i][1],layout[visual][i+2],14);
  1565.  
  1566.          scrolled = TRUE;
  1567.       }
  1568.    }
  1569.  
  1570.    /* adjust only one row of tiles */
  1571.    else 
  1572.    {
  1573. #ifdef ModeX
  1574.       fg_transfer(0,351,vpo,223+vpo,0,hpb,0,0);
  1575. #else
  1576.       fg_vbcopy(0,351,vpo,223+vpo,0,hpb,workvb,workvb);
  1577. #endif
  1578.       tile_orgy--;
  1579.       screen_orgx+=scroll_x;
  1580.       screen_orgy+=(16+scroll_y);
  1581.       for(i = 0; i < 22; i++)
  1582.          put_tile(i,0);
  1583.  
  1584.       adjust_layout_up();
  1585.       scrolled = TRUE;
  1586.    }
  1587.    return(OK);
  1588. }
  1589.  
  1590. /**********************************************************************\
  1591. *                                                                      *
  1592. *  start_bullet                                                        *
  1593. *  Spawn a new object which is a bullet coming from the Quickfire      *
  1594. *  plane, and fired toward the enemy plane.                            *
  1595. *                                                                      *
  1596. \**********************************************************************/
  1597.  
  1598. void near start_bullet()
  1599. {
  1600.    OBJp node;
  1601.  
  1602.    /* nine bullets on the screen is enough */
  1603.    if (nbullets > 9) return;
  1604.  
  1605.    /* allocate space for the bullet node */
  1606.    node = (OBJp)malloc(sizeof(OBJ)+3);
  1607.    if (node == (OBJp)NULL) return;
  1608.  
  1609.    /* the bullet starts 38 pixels ahead of the Quickfire plane */
  1610.    node->x = player->x+38;
  1611.  
  1612.    /* the y position of the bullet depends on whether the plane
  1613.       is upside down or in a roll */
  1614.    if (player->image == fighter[4]) 
  1615.       node->y = player->y+player->image->yoffset-12;
  1616.    else
  1617.       node->y = player->y+player->image->yoffset-2;
  1618.  
  1619.    /* assign some values */
  1620.    node->xspeed = 27;
  1621.    node->yspeed = 0;
  1622.    node->frame = 0;
  1623.    node->tile_xmin = 2;
  1624.    node->tile_xmax = 21;
  1625.    node->tile_ymin = 0;
  1626.    node->tile_ymax = 14;
  1627.  
  1628.    /* assign the sprite */
  1629.    node->image = sprite[4];
  1630.  
  1631.    /* assign the action function */
  1632.    node->action = &bullet_go;
  1633.  
  1634.    /* insert the new object at the top of the linked list */
  1635.    if (bottom_node == (OBJp)NULL)
  1636.    {
  1637.       bottom_node = node;
  1638.       node->prev = (OBJp)NULL;
  1639.    }
  1640.    else
  1641.    {
  1642.       node->prev = top_node;
  1643.       node->prev->next = node;
  1644.    }
  1645.    top_node = node;
  1646.    node->next = (OBJp)NULL;
  1647.  
  1648.    /* increment the bullet count */
  1649.    nbullets++;
  1650.  
  1651.    /* add another bullet if flying sideways */
  1652.    if (player->image == fighter[2] || player->image == fighter[6])
  1653.    {
  1654.       node = (OBJp)malloc(sizeof(OBJ)+3);
  1655.          if (node == (OBJp)NULL) return;
  1656.  
  1657.       node->x = player->x+38;
  1658.       node->y = player->y+player->image->yoffset-26;
  1659.  
  1660.       node->xspeed = 27;
  1661.       node->yspeed = 0;
  1662.       node->frame = 0;
  1663.       node->tile_xmin = 2;
  1664.       node->tile_xmax = 21;
  1665.       node->tile_ymin = 0;
  1666.       node->tile_ymax = 14;
  1667.       node->image = sprite[4];
  1668.       node->action = &bullet_go;
  1669.  
  1670.       if (bottom_node == (OBJp)NULL)
  1671.       {
  1672.          bottom_node = node;
  1673.          node->prev = (OBJp)NULL;
  1674.       }
  1675.       else
  1676.       {
  1677.          node->prev = top_node;
  1678.          node->prev->next = node;
  1679.       }
  1680.       top_node = node;
  1681.       node->next = (OBJp)NULL;
  1682.       nbullets++;
  1683.    }
  1684.  
  1685.    /* or if within a roll */
  1686.    else if (player->image == fighter[1] || player->image == fighter[3] ||
  1687.             player->image == fighter[5] || player->image == fighter[7])
  1688.    {
  1689.       node = (OBJp)malloc(sizeof(OBJ)+3);
  1690.       if (node == (OBJp)NULL) return;
  1691.  
  1692.       node->x = player->x+38;
  1693.       node->y = player->y+player->image->yoffset-20;
  1694.  
  1695.       node->xspeed = 27;
  1696.       node->yspeed = 0;
  1697.       node->frame = 0;
  1698.       node->tile_xmin = 2;
  1699.       node->tile_xmax = 21;
  1700.       node->tile_ymin = 0;
  1701.       node->tile_ymax = 14;
  1702.       node->image = sprite[4];
  1703.       node->action = &bullet_go;
  1704.  
  1705.       if (bottom_node == (OBJp)NULL)
  1706.       {
  1707.          bottom_node = node;
  1708.          node->prev = (OBJp)NULL;
  1709.       }
  1710.       else
  1711.       {
  1712.          node->prev = top_node;
  1713.          node->prev->next = node;
  1714.       }
  1715.       top_node = node;
  1716.       node->next = (OBJp)NULL;
  1717.       nbullets++;
  1718.    }
  1719. }
  1720.  
  1721. /**********************************************************************\
  1722. *                                                                      *
  1723. *  start_enemy_bullet                                                  *
  1724. *  This is where the enemy fighter spawns a bullet to shoot at you.    *
  1725. *                                                                      *
  1726. \**********************************************************************/
  1727.  
  1728. void near start_enemy_bullet(OBJp objp)
  1729. {
  1730.    OBJp node;
  1731.  
  1732.    /* 9 enemy bullets is enough */
  1733.    if (nenemy_bullets > 9) return;
  1734.  
  1735.    /* allocate space for the new object */
  1736.    node = (OBJp)malloc(sizeof(OBJ)+3);
  1737.    if (node == (OBJp)NULL) return;
  1738.  
  1739.    /* assign appropriate values */
  1740.    node->xspeed = -12;
  1741.    node->yspeed = 0;
  1742.    node->frame = 0;
  1743.    node->tile_xmin = 2;
  1744.    node->tile_xmax = 21;
  1745.    node->tile_ymin = 0;
  1746.    node->tile_ymax = 14;
  1747.  
  1748.    /* what kind of bullet is fired depends on what kind of plane it is */
  1749.    if (objp->image == sprite[5])
  1750.    {
  1751.       node->image = sprite[6];
  1752.       node->y = objp->y-22;
  1753.       node->x = objp->x;
  1754.    }
  1755.    else
  1756.    {
  1757.       node->image = sprite[8];
  1758.       node->y = objp->y-2;
  1759.       node->x = objp->x+26;
  1760.    }
  1761.  
  1762.    /* assign the action function */
  1763.    node->action = &enemy_bullet_go;
  1764.  
  1765.    /* insert this object at the top of the linked list */
  1766.    if (bottom_node == (OBJp)NULL )
  1767.    {
  1768.       bottom_node = node;
  1769.       node->prev = (OBJp)NULL;
  1770.    }
  1771.    else
  1772.    {
  1773.       node->prev = top_node;
  1774.       node->prev->next = node;
  1775.    }
  1776.    top_node = node;
  1777.    node->next = (OBJp)NULL;
  1778.  
  1779.    /* increment the enemy bullet count */
  1780.    nenemy_bullets++;
  1781. }
  1782.  
  1783. /**********************************************************************\
  1784. *                                                                      *
  1785. *  start_enemy                                                         *
  1786. *  This is where we start a new enemy fighter. This happens at random  *
  1787. *  intervals when there are less than 5 fighters in the playfield.     *
  1788. *                                                                      *
  1789. \**********************************************************************/
  1790.  
  1791. void near start_enemy()
  1792. {
  1793.    OBJp node;
  1794.  
  1795.    /* if there are already too many enemies on the screen, return */
  1796.    if (nenemies >= MAXENEMIES) return;
  1797.  
  1798.    /* allocate space for this object */
  1799.    node = (OBJp)malloc(sizeof(OBJ)+3);
  1800.    if (node == (OBJp)NULL) return;
  1801.  
  1802.    /* assign values to the variables */
  1803.    node->xspeed = 7;
  1804.    node->frame = 1;
  1805.    node->yspeed = irandom(-3,3);
  1806.    node->tile_xmin = 2;
  1807.    node->tile_xmax = 21;
  1808.    node->tile_ymin = 0;
  1809.    node->tile_ymax = 14;
  1810.  
  1811.    /* every 4th airplane is a big sprite */
  1812.    if (random_number%4 == 0)
  1813.       node->image = sprite[5];
  1814.    else
  1815.       node->image = sprite[7];
  1816.  
  1817.    /* assign the action function */
  1818.    node->action = &enemy_go;
  1819.  
  1820.    /* there is no attached sprite until the enemy starts to explode */
  1821.    node->attached_sprite = (OBJp)NULL;
  1822.  
  1823.    /* start flying at the far right side of the screen */
  1824.    node->x = tile_orgx*16+280;
  1825.  
  1826.    /* sometimes at the top, sometimes at the bottom */
  1827.    if (random_number%2 == 0)
  1828.      node->y = tile_orgy*16+239;
  1829.    else
  1830.      node->y = tile_orgy*16 + node->image->height;
  1831.  
  1832.    /* insert this node at the top of the linked list */
  1833.    if (bottom_node == (OBJp)NULL )
  1834.    {
  1835.       bottom_node = node;
  1836.       node->prev = (OBJp)NULL;
  1837.    }
  1838.    else
  1839.    {
  1840.       node->prev = top_node;
  1841.       node->prev->next = node;
  1842.    }
  1843.    top_node = node;
  1844.    node->next = (OBJp)NULL;
  1845.  
  1846.    /* also insert this node in the enemy array */
  1847.    enemy[nenemies] = node;
  1848.  
  1849.    /* increment the enemy count */
  1850.    nenemies++;
  1851. }
  1852.  
  1853. /**********************************************************************\
  1854. *                                                                      *
  1855. *  start_explosion                                                     *
  1856. *  This function spawns a new object with is the explosion which will  *
  1857. *  be attached to one of the enemy fighters.                           *
  1858. *                                                                      *
  1859. \**********************************************************************/
  1860.  
  1861. void near start_explosion(OBJp objp)
  1862. {
  1863.    OBJp node;
  1864.  
  1865.    /* allocate space for the object */
  1866.    node = (OBJp)malloc(sizeof(OBJ)+3);
  1867.    if (node == (OBJp)NULL) return;
  1868.  
  1869.    /* assign values to the variables */
  1870.    node->xspeed = 0;
  1871.    node->yspeed = objp->yspeed/2;
  1872.    node->tile_xmin = 2;
  1873.    node->tile_xmax = 21;
  1874.    node->tile_ymin = 0;
  1875.    node->tile_ymax = 14;
  1876.  
  1877.    /* the sprite will be the first frame explosion bitmap */
  1878.    node->image = explosion[0];
  1879.  
  1880.    node->x = objp->x+16;
  1881.    node->y = objp->y-4;
  1882.  
  1883.    /* start the frame at -1 because the action function will increment
  1884.       it to 0 before it is displayed */
  1885.    node->frame = -1;
  1886.  
  1887.    /* Insert at the top of the linked list.  Note, we know this object
  1888.       will never be at the bottom of the list because it must be 
  1889.       attached to another object, which means the list can not be
  1890.       empty. */
  1891.    node->prev = top_node;
  1892.    node->prev->next = node;
  1893.    top_node = node;
  1894.    node->next = (OBJp)NULL;
  1895.  
  1896.    /* set up the links between the explosion and the enemy plane */
  1897.    node->attached_sprite = objp;
  1898.    node->attached_sprite->attached_sprite = node;
  1899.  
  1900.    /* assign the action function */
  1901.    node->action = do_explosion;
  1902.  
  1903.    /* You just scored a hit, so increment the score.  The score depends
  1904.       on how far away the enemy plane was when you hit it. */
  1905.    player_score += (node->attached_sprite->x - tile_orgx*16+1000);
  1906.  
  1907.    /* reset the score object's action function */
  1908.    score->action = &new_score;
  1909.  
  1910.    /* increment the hit counter */
  1911.    nhits++;
  1912. }
  1913.  
  1914. /**********************************************************************\
  1915. *                                                                      *
  1916. *  start_player_explosion                                              *
  1917. *  When the player gets hit there is a little explosion sprite that    *
  1918. *  causes no damage.  Spawn it here.                                   *
  1919. *                                                                      *
  1920. \**********************************************************************/
  1921.  
  1922. void near start_player_explosion(OBJp objp)
  1923. {
  1924.    OBJp node;
  1925.  
  1926.    /* allocate space for the new object */
  1927.    node = (OBJp)malloc(sizeof(OBJ));
  1928.    if (node == (OBJp)NULL) return;
  1929.  
  1930.    /* assign some variables */
  1931.    node->xspeed = player->xspeed;
  1932.    node->yspeed = player->yspeed;
  1933.    node->tile_xmin = 2;
  1934.    node->tile_xmax = 21;
  1935.    node->tile_ymin = 0;
  1936.    node->tile_ymax = 14;
  1937.  
  1938.    /* assign the sprite */
  1939.    node->image = explosion[0];
  1940.  
  1941.    node->x = objp->x;
  1942.    node->y = objp->y;
  1943.  
  1944.    /* start the frame at -1 because the action function will increment
  1945.       it to 0 before it is displayed */
  1946.    node->frame = -1;
  1947.  
  1948.    /* assign the action function */
  1949.    node->action = &do_player_explosion;
  1950.  
  1951.    /* insert this object at the top of the linked list */
  1952.    node->prev = top_node;
  1953.    node->prev->next = node;
  1954.    top_node = node;
  1955.    node->next = (OBJp)NULL;
  1956.  
  1957.    /* set up the links between the explosion and the Quickfire plane */
  1958.    node->attached_sprite = player;
  1959.    node->attached_sprite->attached_sprite = node;
  1960.  
  1961. }
  1962.  
  1963. /**********************************************************************\
  1964. *                                                                      *
  1965. *  swap                                                                *
  1966. *  Swap the hidden and visual pages by changing the coordinates for    *
  1967. *  the top and bottom of each page, moving the origin of the visual    *
  1968. *  page, and wait for the vertical retrace.                            *
  1969. *                                                                      *
  1970. \**********************************************************************/
  1971.  
  1972. void near swap()
  1973. {
  1974.    int x1,x2,y1,y2;
  1975.  
  1976.    /* vpo = visual page offset, vpb = visual page bottom */
  1977.    /* hpo = hidden page offset, vpb = hidden page bottom */
  1978.  
  1979.    vpo = 240 - vpo;
  1980.    hpo = 240 - hpo;
  1981.  
  1982.    vpb = vpo+239;
  1983.    hpb = hpo+239;
  1984.  
  1985.    /* adjust the globals */
  1986.    visual = !visual;
  1987.    hidden = !hidden;
  1988.  
  1989. #ifdef ModeX
  1990.    /* move the visual screen origin to the correct location on the new
  1991.       visual page */
  1992.    fg_pan(screen_orgx,screen_orgy+vpo);
  1993. #else
  1994.    x1 = screen_orgx;
  1995.    x2 = x1+319;
  1996.    y1 = screen_orgy+vpo;
  1997.    y2 = y1+199;
  1998.    fg_vbpaste(x1,x2,y1,y2,0,199);
  1999. #endif
  2000.  
  2001. }
  2002.  
  2003. /**********************************************************************\
  2004. *                                                                      *
  2005. *  warp                                                                *
  2006. *  Move visible screen to any location on the world map.               *
  2007. *                                                                      *
  2008. \**********************************************************************/
  2009.  
  2010. void near warp(short x,short y)
  2011. {
  2012.    register short i,j;
  2013.  
  2014.    /* calculate the x and y origins in tile space and screen space */
  2015.    if (x < 16)
  2016.    {
  2017.       tile_orgx = x/16;
  2018.       screen_orgx = x%16;
  2019.    }
  2020.    else
  2021.    {
  2022.       tile_orgx = x/16 - 1;
  2023.       screen_orgx = 16 + x%16;
  2024.    }
  2025.    if (y < 16)
  2026.    {
  2027.       tile_orgy = y/16;
  2028.       screen_orgy = y%16;
  2029.    }
  2030.    else if (y > world_maxy - 16)
  2031.    {
  2032.       tile_orgy = y/16-2;
  2033.       screen_orgy = 32 + y%16;
  2034.    }
  2035.    else
  2036.    {
  2037.       tile_orgy = y/16 - 1;
  2038.       screen_orgy = 16 + y%16;
  2039.    }
  2040.  
  2041.    /* rebuild the hidden page by putting all the tiles on it */
  2042.    for (i = 0; i < 22; i++)
  2043.       for (j = 0; j < 15; j++)
  2044.          put_tile(i,j);
  2045.  
  2046.    /* set the layout arrays to all zeros */
  2047.    clear_layout(); 
  2048.  
  2049.    /* make the hidden page visual */
  2050.    swap();
  2051.  
  2052.    /* copy the visual page to the hidden page */
  2053.    page_copy();
  2054. }
  2055.  
  2056.  
  2057.